package com.demo.mq.listener;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MessageListener {

    public static final String QUEUE_NAME = "queue.order";
    public static final String EXCHANGE_NAME = "exchange.direct.order";
    public static final String ROUTING_KEY = "order";

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = QUEUE_NAME, durable = "true"),
            exchange = @Exchange(value = EXCHANGE_NAME, durable = "true"),
            key = {ROUTING_KEY}
        )
    )
    public void processMessage(String data, Message message, Channel channel) throws Exception{
        //获取当前消息的 deliveryTag
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //核心操作
            log.info("消费端，消息内容：{}", data);
            //核心操作成功，返回ACK信息
            channel.basicAck(deliveryTag, false);
        }catch (Exception e){
            //获取当前消息是否是重复投递的
            // redelivered 为 true：说明当前消息已经重复投递过一次了
            // redelivered 为 false：说明当前消息是第一次投递
            Boolean redelivered = message.getMessageProperties().getRedelivered();

            //核心操作失败，返回NACK信息
            //参数一：deliveryTag 表示当前消息的唯一标识
            //参数二：multiple， 消息是否批量处理，注意这里的批量处理是指 从当前deliveryTag开始往前的所有消息，这个值一般指定为false（不批量处理）
            //参数三：requeue，控制消息是否重新放回队列
            //      取值为 true：重新放回队列，broker 会重新投递这个消息
            //      取值为 false：不重新放回队列，broker 会丢弃这个消息
            if(redelivered){
                //如果当前消息已经是重复投递的，说明此前已经重试过一次了,所以 requeue 设置为 false，表示不重新放回队列
                channel.basicNack(deliveryTag, false, false);
            }else {
                //如果当前消息是第一次投递，说明当前代码是第一次抛异常，尚未重试，所以 requeue 设置为 true，表示重新把消息放回队列再投递一次
                channel.basicNack(deliveryTag, false, true);
            }

            // reject 表示拒绝
            // 辨析：basicNack() 和 basicReject() 区别
            //  basicNack() 能控制是否批量操作
            // basicReject() 不能控制是否批量操作
            // channel.basicReject(deliveryTag, true);
        }
    }
}
